package com.yfqy.app.modules.wechat.service;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.yfqy.app.configure.Idgenerator.SnowflakeIdGenerator;
import com.yfqy.app.constant.GeneralConstants;
import com.yfqy.app.domain.entity.OrderInfo;
import com.yfqy.app.domain.entity.OrderPayment;
import com.yfqy.app.domain.entity.ScheduledTask;
import com.yfqy.app.domain.mapper.OrderInfoMapper;
import com.yfqy.app.domain.mapper.OrderPaymentMapper;
import com.yfqy.app.domain.mapper.ScheduledTaskMapper;
import com.yfqy.app.enums.OrderStatus;
import com.yfqy.app.enums.PaymentStatus;
import com.yfqy.app.enums.TaskStatus;
import com.yfqy.app.enums.TaskType;
import com.yfqy.app.modules.order.dto.OrderExtendDTO;
import com.yfqy.app.modules.wechat.config.WechatPayConfig;
import com.yfqy.app.modules.wechat.util.XMLUtil;
import com.yfqy.app.util.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信会调
 */
@Service
@Slf4j
public class WechatCallbackService {

    @Autowired
    private WechatPayConfig wechatPayConfig;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private OrderPaymentMapper orderPaymentMapper;
    @Autowired
    private ScheduledTaskMapper scheduledTaskMapper;
    @Autowired
    private SnowflakeIdGenerator idGenerator;


    @Transactional(rollbackFor = Exception.class)
    public String paymentNotify(HttpServletRequest request) {
        Map<String, String> xmlData = null;
        String orderNo = null;

        try {
            // 1、读取并解析回调参数
            xmlData = parseCallbackParams(request);
            log.info("支付回调数据: {}", xmlData);
            orderNo = xmlData.get("out_trade_no");

            // 2. 验证通信结果
            String returnCode = xmlData.get("return_code");
            if (!"SUCCESS".equals(returnCode)) {
                String returnMsg = xmlData.get("return_msg");
                log.warn("微信支付通信失败: {}", returnMsg);
                return generateErrorResponse(returnMsg);
            }

            // 3. 验证签名
            if (!verifySignature(xmlData)) {
                log.warn("微信支付回调签名验证失败");
                return generateErrorResponse("签名验证失败");
            }

            // 4. 处理业务逻辑
            return processBusinessLogic(xmlData, orderNo);

        } catch (Exception e) {
            log.error("支付回调处理异常, orderNo: {}", orderNo, e);
            // 记录异步补偿任务（在独立事务中）
            if (orderNo != null) {
                recordCompensationTask(orderNo, xmlData, "处理异常: " + e.getMessage());
            }
            return generateErrorResponse("处理失败");
        }
    }

    /**
     * 处理业务逻辑（独立事务）
     */
    @Transactional(rollbackFor = Exception.class)
    public String processBusinessLogic(Map<String, String> params, String orderNo) {
        // 1. 查询订单信息（带悲观锁）
        OrderInfo orderInfo = orderInfoMapper.selectByOrderNoForUpdate(orderNo);
        if (orderInfo == null) {
            log.warn("订单不存在, orderNo: {}", orderNo);
            return generateErrorResponse("订单不存在");
        }

        // 2. 幂等性检查 - 防止重复处理
        if (isAlreadyProcessed(orderInfo, params)) {
            log.info("订单已处理过该回调, orderNo: {}, status: {}", orderNo, orderInfo.getOrderStatus());
            return generateSuccessResponse();
        }

        // 3. 验证业务结果
        String resultCode = params.get("result_code");
        if ("SUCCESS".equals(resultCode)) {
            return handlePaymentSuccess(params, orderInfo);
        } else {
            return handlePaymentFailure(params, orderInfo);
        }
    }

    /**
     * 幂等性检查
     */
    private boolean isAlreadyProcessed(OrderInfo orderInfo, Map<String, String> params) {
        // 检查1：订单状态已经是终态
        if (OrderStatus.PAID.getStatus().equals(orderInfo.getOrderStatus()) ||
                OrderStatus.PAY_FAIL.getStatus().equals(orderInfo.getOrderStatus())) {

            // 检查2：支付记录是否匹配
            OrderPayment payment = orderPaymentMapper.selectByOrderId(orderInfo.getId());
            if (payment != null && payment.getPaymentStatus().equals(PaymentStatus.PAID.getStatus())) {
                String transactionId = params.get("transaction_id");
                if (transactionId != null && transactionId.equals(payment.getPaymentNo())) {
                    return true; // 相同的支付回调已处理
                }
            }
            return true; // 订单已是终态，无论支付记录如何都认为是已处理
        }
        return false;
    }

    /**
     * 处理支付成功
     */
    private String handlePaymentSuccess(Map<String, String> params, OrderInfo orderInfo) {
        try {
            String transactionId = params.get("transaction_id");
            String totalFee = params.get("total_fee");

            log.info("处理支付成功, 订单号: {}, 微信交易号: {}", orderInfo.getOrderNo(), transactionId);

            // 1. 更新订单状态
            updateOrderToPaid(orderInfo, params, totalFee);

            // 2. 更新支付记录
            updatePaymentRecord(orderInfo.getId(), params, true);

            // 3. 记录异步任务
            recordScheduledTask(orderInfo.getId(), params, "支付成功");

            log.info("支付成功处理完成, 订单号: {}", orderInfo.getOrderNo());
            return generateSuccessResponse();

        } catch (Exception e) {
            log.error("处理支付成功失败, 订单号: {}", orderInfo.getOrderNo(), e);
            throw new RuntimeException("支付成功处理失败", e);
        }
    }

    /**
     * 处理支付失败
     */
    private String handlePaymentFailure(Map<String, String> params, OrderInfo orderInfo) {
        try {
            String errCode = params.get("err_code");
            String errCodeDes = params.get("err_code_des");

            log.warn("处理支付失败, 订单号: {}, 错误: {} - {}",
                    orderInfo.getOrderNo(), errCode, errCodeDes);

            // 1. 更新订单状态为支付失败
            updateOrderToPayFailed(orderInfo, params);

            // 2. 更新支付记录
            updatePaymentRecord(orderInfo.getId(), params, false);

            log.info("支付失败处理完成, 订单号: {}", orderInfo.getOrderNo());
            return generateSuccessResponse();

        } catch (Exception e) {
            log.error("处理支付失败异常, 订单号: {}", orderInfo.getOrderNo(), e);
            throw new RuntimeException("支付失败处理异常", e);
        }
    }

    /**
     * 更新订单为已支付状态
     */
    private void updateOrderToPaid(OrderInfo orderInfo, Map<String, String> params, String totalFee) {
        BigDecimal paidAmount = new BigDecimal(totalFee).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);

        OrderInfo updateOrder = new OrderInfo();
        updateOrder.setId(orderInfo.getId());
        updateOrder.setOrderStatus(OrderStatus.PAID.getStatus());
        updateOrder.setPaidAmount(paidAmount);

        OrderExtendDTO extendDTO = new OrderExtendDTO();
        extendDTO.setBeforeStatus(orderInfo.getOrderStatus()); // 记录之前的状态
        extendDTO.setPaymentTime(new Date());
        extendDTO.setTransactionId(params.get("transaction_id"));
        updateOrder.setExtend(JSON.stringify(extendDTO));

        int rows = orderInfoMapper.updateOrderStatusByPayCallback(updateOrder, orderInfo.getOrderStatus());
        if (rows == 0) {
            throw new RuntimeException("更新订单状态失败，可能数据已被修改");
        }
    }

    /**
     * 更新订单为支付失败状态
     */
    private void updateOrderToPayFailed(OrderInfo orderInfo, Map<String, String> params) {
        OrderInfo updateOrder = new OrderInfo();
        updateOrder.setId(orderInfo.getId());
        updateOrder.setOrderStatus(OrderStatus.PAY_FAIL.getStatus());
        updateOrder.setPaidAmount(BigDecimal.ZERO);

        OrderExtendDTO extendDTO = new OrderExtendDTO();
        extendDTO.setBeforeStatus(orderInfo.getOrderStatus());
        extendDTO.setErrorCode(params.get("err_code"));
        extendDTO.setErrorMsg(params.get("err_code_des"));
        updateOrder.setExtend(JSON.stringify(extendDTO));

        int rows = orderInfoMapper.updateOrderStatusByPayCallback(updateOrder, orderInfo.getOrderStatus());
        if (rows == 0) {
            throw new RuntimeException("更新订单状态失败");
        }
    }

    /**
     * 更新支付记录
     */
    private void updatePaymentRecord(Long orderId, Map<String, String> params, boolean success) {
        OrderPayment existingPayment = orderPaymentMapper.selectByOrderId(orderId);
        if (existingPayment == null) {
            log.warn("支付记录不存在, orderId: {}", orderId);
            return;
        }

        OrderPayment updatePayment = OrderPayment.builder()
                .id(existingPayment.getId())
                .paymentNo(params.get("transaction_id"))
                .paymentStatus(success ? PaymentStatus.PAID.getStatus() : PaymentStatus.FAIL.getStatus())
                .notifyData(JSON.stringify(params))
                .paymentTime(new Date())
                .notifyTime(new Date())
                .errorCode(params.get("err_code"))
                .errorMsg(params.get("err_code_des"))
                .build();

        if (success) {
            String totalFee = params.get("total_fee");
            updatePayment.setPaymentAmount(
                    new BigDecimal(totalFee).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
            );
        } else {
            updatePayment.setPaymentAmount(BigDecimal.ZERO);
        }

        int rows = orderPaymentMapper.updatePaymentByPayCallback(updatePayment);
        if (rows == 0) {
            throw new RuntimeException("更新支付记录失败");
        }
    }

    /**
     * 记录异步任务
     */
    public void recordScheduledTask(Long orderId, Map<String, String> params, String remark) {
        try {
            ScheduledTask scheduled = new ScheduledTask();
            scheduled.setId(idGenerator.nextId());
            scheduled.setTaskType(TaskType.WX_PAY_CALLBACK.name());
            scheduled.setBusinessId(orderId);
            scheduled.setBusinessData(JSON.stringify(params));
            scheduled.setExecuteTime(new Date());
            scheduled.setStatus(TaskStatus.WAIT.getStatus());
            scheduled.setMaxRunsCount(GeneralConstants.MAX_RETRY);
            scheduled.setCron(String.format(GeneralConstants.CRON_FORMAT, LocalDateTime.now().getSecond()));
            scheduled.setRemark(remark);
            scheduledTaskMapper.insert(scheduled);
            log.info("记录异步任务成功, orderId: {}, taskId: {}", orderId, scheduled.getId());
        } catch (Exception e) {
            log.error("记录异步任务失败, orderId: {}", orderId, e);
            // 异步任务记录失败不应该影响主事务
        }
    }

    /**
     * 记录补偿任务（独立事务）
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordCompensationTask(String orderNo, Map<String, String> params, String errorMsg) {
        try {
            OrderInfo orderInfo = orderInfoMapper.selectByOrderNo(orderNo);
            if (orderInfo == null) {
                log.warn("记录补偿任务时订单不存在, orderNo: {}", orderNo);
                return;
            }

            ScheduledTask scheduled = new ScheduledTask();
            scheduled.setId(idGenerator.nextId());
            scheduled.setTaskType(TaskType.WX_PAY_CALLBACK.name());
            scheduled.setBusinessId(orderInfo.getId());
            scheduled.setBusinessData(JSON.stringify(params));
            scheduled.setExecuteTime(Date.from(Instant.now().plusMillis(10 * 1000)));
            scheduled.setStatus(TaskStatus.WAIT.getStatus());
            scheduled.setMaxRunsCount(GeneralConstants.MAX_RETRY);
            scheduled.setRemark(errorMsg);
            scheduledTaskMapper.insert(scheduled);
            log.warn("记录补偿任务成功, orderNo: {}, error: {}", orderNo, errorMsg);

        } catch (Exception e) {
            log.error("记录补偿任务失败, orderNo: {}", orderNo, e);
        }
    }


    /**
     * 解析回调参数为Map
     */
    private Map<String, String> parseCallbackParams(HttpServletRequest request) throws IOException {
        Map<String, String> params = new HashMap<>();

        // 读取请求体
        String requestBody = getRequestBody(request);
        log.debug("微信支付回调原始请求体: {}", requestBody);

        if (StringUtils.isBlank(requestBody)) {
            throw new IllegalArgumentException("请求体为空");
        }

        // 解析XML到Map
        try {
            Map<String, String> xmlMap = XMLUtil.parseXmlToMap(requestBody);

            // 转换为String类型的Map
            for (Map.Entry<String, String> entry : xmlMap.entrySet()) {
                if (entry.getValue() != null) {
                    params.put(entry.getKey(), entry.getValue());
                }
            }

        } catch (Exception e) {
            throw new RuntimeException("解析XML请求体失败", e);
        }

        return params;
    }

    /**
     * 获取请求体内容
     */
    private String getRequestBody(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }

        return stringBuilder.toString();
    }


    /**
     * 验证签名（需要实现具体的签名验证逻辑）
     */
    private boolean verifySignature(Map<String, String> params) {
        try {
            // 获取签名
            String sign = params.get("sign");
            if (StringUtils.isBlank(sign)) {
                log.warn("微信支付回调缺少签名");
                return false;
            }

            // 移除sign参数并排序
            Map<String, String> signParams = new HashMap<>(params);
            signParams.remove("sign");

            // 生成待签名字符串（按照微信支付规则）
            String signString = generateSignString(signParams);

            // 使用商户密钥进行签名验证
            String calculatedSign = generateSign(signString);

            return sign.equals(calculatedSign);

        } catch (Exception e) {
            log.error("微信支付会调签名验证异常", e);
            return false;
        }
    }

    /**
     * 生成待签名字符串
     */
    private String generateSignString(Map<String, String> params) {
        // 按照参数名ASCII码从小到大排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);

        StringBuilder signBuilder = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (StringUtils.isNotBlank(value) && !"sign".equals(key)) {
                signBuilder.append(key).append("=").append(value).append("&");
            }
        }

        // 添加商户密钥
        signBuilder.append("key=").append(wechatPayConfig.getApiKey());

        return signBuilder.toString();
    }

    /**
     * 生成签名（实际项目中需要实现具体的签名算法）
     */
    private String generateSign(String signString) {
        try {
            // MD5签名示例，实际根据微信支付要求可能使用HMAC-SHA256
            return DigestUtils.md5DigestAsHex(signString.getBytes(StandardCharsets.UTF_8)).toUpperCase();
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败", e);
        }
    }

    /**
     * 生成成功响应
     */
    private String generateSuccessResponse() {
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    }

    /**
     * 生成错误响应
     */
    private String generateErrorResponse(String returnMsg) {
        return String.format("<xml><return_code><![CDATA[%s]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>",
                "FAIL", returnMsg);
    }
}
